﻿/*
	wp8libflac project
	© Alovchin, 2014
*/

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Windows.Media;
using Windows.Storage;
using Windows.Storage.Streams;
using wp8.Interop;

namespace wp8.MediaHelpers
{
    /// <summary>
    /// Contains <see cref="MediaStreamSource" /> implementation for FLAC playback.
    /// </summary>
    public class FlacStreamSource : MediaStreamSourceBase
    {
        private readonly string _filePath;

        private double _currentTime;
        private long _startPosition;

        private FlacWaveStream _flacWaveStream;
        private IBuffer _instantBuffer;
        
        public FlacStreamSource(string filePath)
        {
            this._filePath = filePath;
            this._instantBuffer = WindowsRuntimeBuffer.Create(4096);
        }

        protected override void CloseMedia()
        {
            this._currentTime = 0;
            base.CloseMedia();
            RaiseStreamComplete();
        }

        protected override void GetDiagnosticAsync(MediaStreamSourceDiagnosticKind diagnosticKind)
        {
            throw new NotImplementedException();
        }

        protected override async void GetSampleAsync(MediaStreamType mediaStreamType)
        {
            var buffer = await this._flacWaveStream.ReadAsync(this._instantBuffer, this._instantBuffer.Capacity, InputStreamOptions.None);

            if (buffer.Length > 0)
            {
                using (var ms = buffer.AsStream())
                {
                    try
                    {
                        var mediaSample = new MediaStreamSample(
                            MediaStreamDescription,
                            ms, 0, ms.Length,
                            (long)(this._currentTime * 10000000),
                            MediaSampleAttributes);

                        this._currentTime += this._flacWaveStream.GetDurationFromBufferSize(4096);
                        
                        ReportGetSampleCompleted(mediaSample);
                    }
                    catch (NullReferenceException)
                    {
                        ReportGetSampleCompleted(EndOfMediaStreamSample);
                    }
                }
            }
            else
            {
                ReportGetSampleCompleted(EndOfMediaStreamSample);
                this._flacWaveStream.Seek(this._startPosition);
                this._currentTime = 0;
            }

            this._instantBuffer.Length = 0;
        }

        protected override void OpenMediaAsync()
        {
            this._flacWaveStream = new FlacWaveStream(this._filePath);

            FlacStreamInfo streamInfo = this._flacWaveStream.GetStreamInfo();
            var mediaSourceAttributes = new Dictionary<MediaSourceAttributesKeys, string>
            {
                {MediaSourceAttributesKeys.CanSeek, true.ToString(CultureInfo.InvariantCulture)},
                {MediaSourceAttributesKeys.Duration, ((long)(streamInfo.Duration * 10000000)).ToString(CultureInfo.InvariantCulture)}
            };

            this._startPosition = this._flacWaveStream.Position;
            this._currentTime = 0;
            MediaStreamAttributes[MediaStreamAttributeKeys.CodecPrivateData] =
                streamInfo.CodecData.ToUpperInvariant();

            ReportOpenMediaCompleted(mediaSourceAttributes, new[] {MediaStreamDescription});
        }

        protected override void SeekAsync(long seekToTime)
        {
            long bufferSize = this._flacWaveStream.GetBufferSizeFromDuration(seekToTime);
            this._flacWaveStream.Seek(this._startPosition + bufferSize);
            this._currentTime = seekToTime;
            ReportSeekCompleted(seekToTime);
        }

        protected override void SwitchMediaStreamAsync(MediaStreamDescription description)
        {
            throw new NotImplementedException();
        }

        protected override void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                if (this._flacWaveStream != null)
                    this._flacWaveStream.Dispose();

                this._instantBuffer = null;
            }
        }
    }
}